
#ifndef __LOADLIBRARY_H__
#define __LOADLIBRARY_H__
#pragma once
#include <Windows.h>
#include <tchar.h>
#include <assert.h>
#include <string>
#include <tchar.h>
#if (defined(_UNICODE) || defined (UNICODE))
namespace std { typedef ::std::basic_string<wchar_t> tstring; }
#else
namespace std { typedef ::std::basic_string<char> tstring; }
#endif

#if 0
class IHelloDll
{
	DLL_LOADER_DECL(IHelloDll, HelloDll);
	//extern "C" HELLODLL_API int __stdcall HelloDll_Ver();
	DLL_LOADER_ENTRY_R_A0(int, __stdcall, HelloDll_Ver, 0);
	//extern "C" HELLODLL_API int __stdcall HelloDll_Ver2(int nFlag);
	DLL_LOADER_ENTRY_R_A1(int, __stdcall, HelloDll_Ver, 0,int, nFlag);
};
#endif

class CLoadLibrary
{
public:
	HINSTANCE m_hInst;
	CLoadLibrary(HINSTANCE hInst = NULL) : m_hInst(hInst){}
	CLoadLibrary(LPCTSTR pstrFileName) : m_hInst(NULL){Load(pstrFileName);}
	CLoadLibrary(std::tstring& pstrFileName) : m_hInst(NULL){Load(pstrFileName.c_str());}
	~CLoadLibrary(){Free();}
public:
	operator HINSTANCE() const{ return m_hInst; }
	BOOL IsLoaded() const { return m_hInst != NULL; }
public:
	static std::tstring GetAppPath(){
		TCHAR szBuff[MAX_PATH];
		GetModuleFileName(NULL, szBuff, MAX_PATH);
		for (int i = _tcslen(szBuff); i; i--){
			if (szBuff[i] == _T('\\')){
				szBuff[i] = _T('\0');
				break;
			}
		}
		TCHAR szBuff2[MAX_PATH] = {0};
		std::tstring strFullPath;
		if (_tfullpath(szBuff2, szBuff, MAX_PATH) != NULL){
			strFullPath = szBuff2;
		}
		return strFullPath;
	}

	BOOL Load(LPCTSTR pstrFileName, DWORD dwFlags = 0)
	{
		assert(!::IsBadStringPtr(pstrFileName, MAX_PATH));
		Free();
		m_hInst = ::LoadLibraryEx(pstrFileName, NULL, dwFlags);
		return m_hInst != NULL;
	}

	BOOL Load(std::tstring pstrFileName, DWORD dwFlags = 0)
	{
		assert(!::IsBadStringPtr(pstrFileName.c_str(), MAX_PATH));
		Free();
		m_hInst = ::LoadLibraryEx(pstrFileName.c_str(), NULL, dwFlags);
		return m_hInst != NULL;
	}

	void Free()
	{
		if( IsLoaded() ) {
			::FreeLibrary(m_hInst);
			m_hInst = NULL;
		}
	}

	HINSTANCE Detach()
	{
		HINSTANCE hInst = m_hInst;
		m_hInst = NULL;
		return hInst;
	}

	FARPROC GetProcAddress(LPCSTR pszFuncName) const
	{ 
		assert(!::IsBadStringPtrA(pszFuncName,-1));
		assert(IsLoaded()); 
		return ::GetProcAddress(m_hInst, pszFuncName);
	}

	BOOL GetFileName(LPTSTR pstrFilename, DWORD cchMax = MAX_PATH) const
	{
		assert(IsLoaded());
		return ::GetModuleFileName(m_hInst, pstrFilename, cchMax);
	}
}; 

#if 0
class IHelloDll
{
////////////////////////////////////////////////////////////////////////////////////
	DLL_LOADER_DECL(IHelloDll, HelloDll);
#if 0
protected:
	CLoadLibrary m_oMod;
	bool m_bInit;
	IHelloDll() { m_bInit = false; }
public:
	static IHelloDll& Inst()
	{
		static IHelloDll g_oInst;
		if (!g_oInst.m_bInit)
		{
			g_oInst.m_bInit = true;
			g_oInst.m_oMod.Load(CLoadLibrary::GetAppPath() + _T("\\") + _T("HelloDll") + _T(".dll"));
		}
		return g_oInst;
	}
#endif

////////////////////////////////////////////////////////////////////////////////////
	DLL_LOADER_ENTRY_R_A0(int, __stdcall, HelloDll_Ver, 0);
#if 0/
public:
	typedef int(__stdcall* PFN_HelloDll_Ver)();
	int HelloDll_Ver()
	{
		static PFN_HelloDll_Ver pFn_HelloDll_Ver(NULL);
		static bool bHaveLoad(false);
		if (m_oMod.IsLoaded() && (!bHaveLoad))
		{
			bHaveLoad = true;
			pFn_HelloDll_Ver = (PFN_HelloDll_Ver)m_oMod.GetProcAddress("HelloDll_Ver");
		}
		return pFn_HelloDll_Ver ? pFn_HelloDll_Ver() : 0;
	}
#endif


};

#endif


#define DLL_LOADER_DECL(ClassName,DllName) \
protected:\
	CLoadLibrary m_oMod;\
	bool m_bInit;\
	ClassName(){m_bInit=false;}\
public:\
	static ClassName& Inst()\
	{\
		static ClassName g_oInst;\
		if (!g_oInst.m_bInit)\
		{\
			g_oInst.m_bInit = true;\
			g_oInst.m_oMod.Load(CLoadLibrary::GetAppPath() + _T("\\")+_T(#DllName)+_T(".dll"));\
		}\
		return g_oInst;\
	}

#define DLL_LOADER_HANDLE_DECL(ClassName) \
protected:\
	CLoadLibrary m_oMod;\
	bool m_bInit;\
	ClassName(){m_bInit=false;}\
public:\
	static ClassName& Inst()\
	{\
		static ClassName g_oInst;\
		return g_oInst;\
	}\
	bool LoadTheDll(LPCTSTR pszTheDll)\
	{\
	if (!ClassName::Inst().m_bInit)\
		{\
			ClassName::Inst().m_bInit = true;\
			return ClassName::Inst().m_oMod.Load(pszTheDll);\
		}\
		return false;\
	}	
	
#define DLL_LOADER_ENTRY_V_A0(FUNDESC,FUNNAME)\
	public:\
	void FUNNAME()\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)();\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM();\
	}

#define DLL_LOADER_ENTRY_V_A1(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name);\
	}

#define DLL_LOADER_ENTRY_V_A2(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name,FUNA2Name);\
	}

#define DLL_LOADER_ENTRY_V_A3(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name);\
	}

#define DLL_LOADER_ENTRY_V_A4(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name, FUNA4Type FUNA4Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name);\
	}

#define DLL_LOADER_ENTRY_V_A5(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name,FUNA5Type,FUNA5Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name,FUNA5Type FUNA5Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name, FUNA4Type FUNA4Name,FUNA5Type FUNA5Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name,FUNA5Name);\
	}

#define DLL_LOADER_ENTRY_V_A6(FUNDESC,FUNNAME,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name,FUNA5Type,FUNA5Name,FUNA6Type,FUNA6Name)\
	public:\
	void FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name,FUNA5Type FUNA5Name,FUNA6Type FUNA6Name)\
	{\
		typedef void (FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type, FUNA3Name, FUNA4Type, FUNA4Name,FUNA5Type FUNA5Name,FUNA6Type FUNA6Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		if(pfn_##FUNNAM) pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name,FUNA5Name,FUNA6Name);\
	}

#define DLL_LOADER_ENTRY_R_A0(FUNRET,FUNDESC,FUNNAME,FUNRVAL)\
	public:\
	FUNRET FUNNAME()\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)();\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM():FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A1(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name):FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A2(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name,FUNA2Name):FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A3(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name):FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A4(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name, FUNA4Type FUNA4Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name):FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A5(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name,FUNA5Type,FUNA5Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name,FUNA5Type FUNA5Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name, FUNA4Type FUNA4Name, FUNA5Type FUNA5Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name,FUNA5Name):FUNRVAL;\
	}

#define DLL_LOADER_ENTRY_R_A6(FUNRET,FUNDESC,FUNNAME,FUNRVAL,FUNA1Type,FUNA1Name,FUNA2Type,FUNA2Name,FUNA3Type,FUNA3Name,FUNA4Type,FUNA4Name,FUNA5Type,FUNA5Name,FUNA6Type,FUNA6Name)\
	public:\
	FUNRET FUNNAME(FUNA1Type FUNA1Name,FUNA2Type FUNA2Name,FUNA3Type FUNA3Name,FUNA4Type FUNA4Name,FUNA5Type FUNA5Name,FUNA6Type FUNA65Name)\
	{\
		typedef FUNRET(FUNDESC *PFN_##FUNNAME)(FUNA1Type FUNA1Name, FUNA2Type FUNA2Name, FUNA3Type FUNA3Name, FUNA4Type FUNA4Name, FUNA5Type FUNA5Name, FUNA6Type FUNA6Name);\
		static PFN_##FUNNAME pfn_##FUNNAM(NULL);\
		static bool bHaveLoad(false);\
		if (m_oMod.IsLoaded() && (!bHaveLoad))\
		{\
			bHaveLoad = true;\
			pfn_##FUNNAM = (PFN_##FUNNAME)m_oMod.GetProcAddress(#FUNNAME);\
		}\
		return pfn_##FUNNAM? pfn_##FUNNAM(FUNA1Name,FUNA2Name,FUNA3Name,FUNA4Name,FUNA5Name,FUNA6Name):FUNRVAL;\
	}
#endif//__LOADLIBRARY_H__